package uk.ac.bbk.dcs.muc;

/**
 * Copyright [2011] [Sean Howes and Aruna Gunasekera]
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

/**
 * All the services are run by this class
 * 
 * Author: Aruna Gunasekera and Sean Howes
 */

import static uk.ac.bbk.dcs.muc.EventSqlLiteHelper.writeRecordToDb;

import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import android.view.Gravity;
import android.widget.Toast;

public class MobileService extends Service implements LocationListener {

	private LocationManager locationManager = null;
	private SensorManager sensorManager;
	private SoundRecorderService recordingService;
	private EventSqlLiteHelper helper = new EventSqlLiteHelper(this);
	private SQLiteDatabase database;

	// Values for toast pop up
	private String lightValues;
	private String orientValues;
	private String accelValues;
	private String locationValues;
	// How often the timer task will poll sensors
	private Integer timeFreqValue;
	// The inital delay before we start polling
	private static long DELAY_INTERVAL = 500;
	// A holder for the current location found by the gps
	private Location loc;

	private Timer timer;
	private Handler handler;

	@Override
	public void onCreate() {
		Log.d("service", "***********service creation*******************");
		database = helper.getWritableDatabase();
		handler = new Handler(Looper.getMainLooper());
		recordingService = new SoundRecorderService(database);
		recordingService.beginRecording();
		locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		locationManager.requestLocationUpdates("gps", 0, 0, this);
		helper.dropAndCreate(database);
	}

	@Override
	public void onStart(Intent intent, int startId) {
		super.onStart(intent, startId);
		Bundle bundle = intent.getExtras();
		timeFreqValue = (Integer) bundle.getInt("timeFreq");

		if (timeFreqValue > 0) {
			timeFreqValue = timeFreqValue * 1000;
		} else {
			timeFreqValue = 1000;
		}
		timer = new Timer("service-timer", true);
		timer.scheduleAtFixedRate(taskTimer, DELAY_INTERVAL, timeFreqValue);
		Log.d("service", "###############service start################## " + timeFreqValue);

	}

	/**
	 * On destroy we clean up the timer task and stop the sound recording,
	 * unregister sensors etc
	 */
	@Override
	public void onDestroy() {
		super.onDestroy();
		sensorManager.unregisterListener(listener);
		recordingService.stop();

		if (helper != null) {
			helper.close();
		}

		if (timer != null)
			timer.cancel();
		Log.i(getClass().getSimpleName(), "Timer stopped...");

	}

	/**
	 * This displays the nice little popup with some of the values in it
	 */
	public void displayMessage() {
		handler.post(new Runnable() {

			public void run() {
				//
				Toast toast = Toast.makeText(getApplicationContext(), locationValues + "  " + "Force: " + getTotalForce() + " Light: " + lightValues
						+ " Orient: " + orientValues, Toast.LENGTH_LONG);
				toast.setGravity(Gravity.TOP | Gravity.LEFT, 0, 0);
				toast.show();
			}
		});
	}

	/**
	 * Try to get the location from the gps provider (which is updated by the
	 * listener) if that value is null then fall back to the location helper
	 * which gets from network or worse persitent so we atleast have a value
	 * 
	 * @param now
	 * @throws IOException
	 */
	public void LocationService(Long now) throws IOException {
		if (loc == null) {
			loc = new LocationHelper().getCurrentLocation(locationManager);
		}
		long longId = writeRecordToDb("longitude", now, String.valueOf(loc.getLongitude()), database);
		long latId = writeRecordToDb("latitude", now, String.valueOf(loc.getLatitude()), database);
		long altId = writeRecordToDb("altitude", now, String.valueOf(loc.getAltitude()), database);
		Log.i("location", "ids db " + longId + " " + latId + " " + altId);
		locationValues = "Location is Lat:" + loc.getLongitude() + " Long: " + loc.getLatitude() + " Alt: " + loc.getAltitude();
	}

	/**
	 * Sensor for the light service
	 * 
	 * @throws IOException
	 */

	public void LightService(Long now) throws IOException {
		sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
		sensorManager.registerListener(listener, sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT), 1);
		long id = writeRecordToDb("Light Levels", now, lightValues, database);
		Log.i("light", "db id is " + id + " value is " + lightValues);
		sensorManager.unregisterListener(listener);
	}

	/**
	 * Sensor for the orientation service
	 * 
	 * @throws IOException
	 */

	public void OrientService(Long now) throws IOException {
		sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
		sensorManager.registerListener(listener, sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION), 1);
		long id = writeRecordToDb("Orientation", now, orientValues, database);
		Log.i("orient", "id is " + id + "  orient " + orientValues);
		sensorManager.unregisterListener(listener);
	}

	/**
	 * Sensor for the accellorometer service
	 * 
	 * @throws IOException
	 */

	public void AccelService(Long now) throws IOException {
		sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
		sensorManager.registerListener(listener, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), 1);
		long id = writeRecordToDb("Total Force", now, getTotalForce(), database);
		Log.i("accelerometer", "id is " + id + "  Total Force " + getTotalForce());
		sensorManager.unregisterListener(listener);
	}

	/**
	 * Listener for all the sensors
	 */

	private SensorEventListener listener = new SensorEventListener() {

		@Override
		public void onAccuracyChanged(Sensor arg0, int arg1) {
		}

		@Override
		public void onSensorChanged(SensorEvent event) {
			synchronized (this) {
				switch (event.sensor.getType()) {
				case Sensor.TYPE_ACCELEROMETER:
					accelValues = Float.toString(event.values[0]) + "," + Float.toString(event.values[1]) + "," + Float.toString(event.values[2]);
					break;
				case Sensor.TYPE_ORIENTATION:
					orientValues = Float.toString(event.values[0]) + "," + Float.toString(event.values[1]) + "," + Float.toString(event.values[2]);
					break;
				case Sensor.TYPE_LIGHT:
					lightValues = Float.toString(event.values[0]);
				}
			}
		}
	};

	/**
	 * This is an attempt to do somthing more user freindly with the value from
	 * the accelerometer by turning them into the total force being exterted on
	 * the phone. The sum might be wrong its a long time since A level physics
	 * 
	 * @return
	 */
	public String getTotalForce() {
		String[] xyz = accelValues.split(",");
		Double xAcc = Double.parseDouble(xyz[0]);
		Double yAcc = Double.parseDouble(xyz[1]);
		Double zAcc = Double.parseDouble(xyz[2]);
		return String.valueOf(Math.sqrt((xAcc * xAcc) + (yAcc * yAcc) + (zAcc * zAcc) - SensorManager.GRAVITY_EARTH));
	}

	/**
	 * Timer task passes in now so that we can group the events again later.
	 */
	TimerTask taskTimer = new TimerTask() {
		@Override
		public void run() {
			try {
				Long now = System.currentTimeMillis();
				LocationService(now);
				LightService(now);
				OrientService(now);
				AccelService(now);
				displayMessage();
				recordingService.recordMaxAmplitude(now);

			} catch (Exception e) {
				Log.e("Time task", "Task errors", e);
			}
		}
	};

	@Override
	public void onLocationChanged(Location location) {
		this.loc = location;
	}

	/* No implementation as we dont want to do anything with these events */
	
	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}
	
	@Override
	public void onProviderDisabled(String provider) {
	}

	@Override
	public void onProviderEnabled(String provider) {
	}

	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
	}

}
